/*
 * @Descripttion:常用工具类
 */
import dayjs from 'dayjs';

import type { OpUnitType } from 'dayjs';

export * from './validate';

type StrOrNum = string | number

/**
 * @description: 这里根据移动端原生的 userAgent 来判断当前是 Android 还是 ios
 * @param {*} string
 * @return {*}
 */
export const getDeviceType = (): string => {
  const u = navigator.userAgent;
  if (u.indexOf('Android') > -1 || u.indexOf('Adr') > -1) {
    return 'android';
  }
  return 'ios';
};

/**
 * @description: 利用dayjs格式化时间
 * @param {Date} time 时间
 * @param {string} format 格式 YYYY-MM-DD HH:mm:ss
 * @return {*}
 */
export const formatDate = (time: Date | string | number, format = 'YYYY-MM-DD HH:mm:ss') => dayjs(time).format(format);

/**
 * @description: 获取第一天
 * @param {Date} time 时间
 * @param {key} time 时间
 * @param {string} format 格式 YYYY-MM-DD HH:mm:ss
 * @return {*}
 */
export const getFirstDayByKey = (
  time: Date | string | number,
  key: OpUnitType,
  format = 'YYYY-MM-DD HH:mm:ss'
) => dayjs(time).startOf(key).format(format);

/**
 * @description: 获取最后一天
 * @param {Date} time 时间
 * @param {key} time 时间
 * @param {string} format 格式 YYYY-MM-DD HH:mm:ss
 * @return {*}
 */
export const getLastDayByKey = (
  time: Date | string | number,
  key: OpUnitType,
  format = 'YYYY-MM-DD HH:mm:ss'
) => dayjs(time).endOf(key).format(format);

/**
 * @description: 判断传入内容是否是 undefined、null或者空字符串
 * @param {array} str
 */
export const isEmpty = (...str: Array<any>): boolean => str.some((i) => i === undefined || i === null || i === '');

/**
 * @description: 复制
 * @param {*} copyInfo
 */
export const copyHandle = (copyInfo: string): void => {
  const $textarea = document.createElement('textarea');
  $textarea.value = copyInfo;
  document.body.appendChild($textarea);
  $textarea.select();
  document.execCommand('Copy'); // 执行浏览器复制命令
  $textarea.style.display = 'none';
  document.body.removeChild($textarea);
};

/**
 * @description: 通过图片获取网速
 * @param {*} fileSize 单位KB
 * @return B/ms
 */
export const getSpeedWithImg = (imgUrl: string, fileSize: number): any => new Promise((resolve) => {
  const start = Date.now();
  let end = null;
  const img = document.createElement('img');
  img.onload = () => {
    end = Date.now();
    const speed = (fileSize * 1000) / (end - start);
    resolve(speed);
  };
  img.src = imgUrl;
}).catch((err) => { throw err; });

/**
 * @description: 获取两个日期之间的所有日期
 * @param {Date} startTime GMT日期格式
 * @param {Date} endTime GMT日期格式
 */
export const getAllDateCN = (startTime: Date, endTime: Date) => {
  const dateAll = [];
  let i = 0;
  while (endTime.getTime() - startTime.getTime() >= 0) {
    // 获取日期和时间
    const year = startTime.getFullYear();
    const month = startTime.getMonth() + 1;
    const day = startTime.getDate();
    const time = startTime.toLocaleTimeString();

    const fullMonth = month < 10 ? `0${month}` : month;
    const fullDay = day < 10 ? `0${day}` : day;
    dateAll[i] = `${year}-${fullMonth}-${fullDay} ${time}`;

    // 天数+1
    startTime.setDate(startTime.getDate() + 1);
    i += 1;
  }
  return dateAll;
};

/**
 * @description: 求两个数组的交集
 * @param {StrOrNum} arrayFirst
 * @param {StrOrNum} arraySecond
 */
export const intersection = (arrayFirst: StrOrNum[], arraySecond: StrOrNum[]) => {
  const matched = arrayFirst.filter((af: string | number) => arraySecond.some((as: string | number) => af === as));
  return matched;
};

/**
 * @description: 格式化参数，去掉参数字段中的不合规的值
 * @param {*} _paramsObj 参数
 * @return {*}
 */
export function formatParams(_paramsObj: any) {
  if (Object.prototype.toString.call(_paramsObj) === '[object Object]') {
    const params: any = {};
    for (const key in _paramsObj) {
      if (Object.prototype.hasOwnProperty.call(_paramsObj, key)) {
        const item = _paramsObj[key];
        if (item instanceof Array) {
          if (item.length) {
            params[key] = item;
          }
        } else if (typeof item !== 'undefined' && typeof item !== 'function' && item !== null && item !== '') {
          params[key] = item;
        }
      }
    }
    return params;
  }
  return _paramsObj;
}

/**
 * @description: 将标准时间转换为时间戳
 * @param {type}
 * @return:
 */
export function changeDateToTimestamp(dateStr: string) {
  if (dateStr) {
    const dateTime = dateStr.replace(/-/g, '/');
    return Date.parse(dateTime);
  }
  return '';
}

/**
 * @description: 获取数据类型
 * @param {any} obj
 * @return {*}
 */
export function getType(obj: any): any {
  const toString: any = Object.prototype.toString;
  const map: any = {
    '[object Boolean]': 'boolean',
    '[object Number]': 'number',
    '[object String]': 'string',
    '[object Function]': 'function',
    '[object Array]': 'array',
    '[object Date]': 'date',
    '[object RegExp]': 'regExp',
    '[object Undefined]': 'undefined',
    '[object Null]': 'null',
    '[object Object]': 'object',
  };
  return map[toString.call(obj)];
}

/**
 * @description: 将扁平数组数据转换为树形结构数据
 * @param {*} data 元数据
 * @param {*} id
 * @param {*} pid
 */
export const arrayToTreeData = (data: TypeObject[], id = 'id', pid = 'parentId') => {
  if (!data.length) { return []; }
  const res = [];
  const temp: TypeObject = {};
  for (let i = 0; i < data.length; i++) {
    temp[data[i][id]] = data[i];
  }
  for (let k = 0; k < data.length; k++) {
    data[k]._projectCard = data[k].sourceCode
      ? data[k].sourceCode.split('/')[data[k].sourceCode.split('/').length - 1]
      : null; // 冗余代码：处理项目树时，解析出其对应的身份证id，方便第三方系统跳转至此默认选中项目使用

    if (temp[data[k][pid]] && data[k][id] !== data[k][pid]) {
      if (!temp[data[k][pid]].children) {
        temp[data[k][pid]].children = [];
      }
      if (!temp[data[k][pid]]._level) {
        temp[data[k][pid]]._level = 1;
      }
      data[k]._level = temp[data[k][pid]]._level + 1;
      temp[data[k][pid]].children.push(data[k]);
    } else {
      res.push(data[k]);
    }
  }
  return res;
};

/**
 * @description: 将树形结构数据转换为扁平数组数据
 * @param {TypeObject} tree
 * @return {*}
 */
export const treeDataToArray = <T = TypeObject>(tree: T[], props = { children: 'children' }) => {
  let res: T[] = [];
  for (const item of tree) {
    const { [props.children]: children, ...i } = (item as any);
    if (children && children.length) {
      res = res.concat(treeDataToArray(children));
    }
    res.push(i);
  }
  return res;
};

/**
 * @description: 获取树形结构数据的所有叶子节点
 * @param {TypeObject} tree
 * @return {*}
 */
export const getLeafs = <T = TypeObject>(treeData: T[], props = { children: 'children' }) => {
  if (!treeData || treeData.length === 0) {
    return [];
  }
  const leafs: T[] = [];
  const recursion = (data: T[]) => {
    data.forEach((item) => {
      if ((item as any)[props.children] && (item as any)[props.children].length > 0) {
        recursion((item as any)[props.children]);
      } else {
        leafs.push(item);
      }
    });
  };
  recursion(treeData);
  return leafs;
};

/**
 * @description: 根据指定的key对数组中对象去重
 */
export const toDeduplicate = (list: TypeObject[], key: string) => {
  const obj: TypeObject = {};
  return list.reduce((cur, next) => {
    if (!obj[next[key]]) {
      obj[next[key]] = true;
      cur.push(next);
    }
    return cur;
  }, []);
};

/**
 * @description: 函数防抖
 */
export const debounce = (func: Func, time: number, immediate = false) => {
  let timer: number | null = null;
  return (...args: any) => {
    if (timer) {
      clearInterval(timer);
    }
    if (immediate) {
      if (!timer) {
        // eslint-disable-next-line no-invalid-this
        func.apply(this, args);
      }
      timer = window.setTimeout(() => {
        timer = null;
      }, time);
    } else {
      timer = window.setTimeout(() => {
        // eslint-disable-next-line no-invalid-this
        func.apply(this, args);
      }, time);
    }
  };
};

/**
 * @description: 函数节流
 */
export const throttle = (func: Func, time: number, immediate = false) => {
  if (immediate) {
    let prevTime = 0;
    return (...args: any) => {
      const nowTime = Date.now();
      if (nowTime - prevTime >= time) {
        // eslint-disable-next-line no-invalid-this
        func.apply(this, args);
        prevTime = nowTime;
      }
    };
  }
  let timer: number | null = null;
  return (...args: any) => {
    if (!timer) {
      // eslint-disable-next-line no-invalid-this
      func.apply(this, args);
      timer = window.setTimeout(() => {
        if (timer) { clearInterval(timer); }
        timer = null;
      }, time);
    }
  };
};

/**
 * @description: 解析URL参数通
 * @param {string} url
 * @param {*} string
 * @return {*}
 */
export const parseUrlParams = (url: string): Record<string, string | string[]> => {
  try {
    const urlObj = new URL(url);
    const params = new URLSearchParams(urlObj.search);
    const parsedParams: Record<string, string | string[]> = {};

    for (const [key, value] of params.entries()) {
      // 如果值是逗号分隔的字符串，将其转换为数组
      parsedParams[key] = value.includes(',')
        ? value.split(',').map((v) => v.trim())
        : value;
    }

    return parsedParams;
  } catch (error) {
    console.error('parseUrlParams error:', error);
    return {};
  }
};

/**
 * @description: 将一个对象转换为URL查询参数字符串
 * @param {Record} obj
 * @param {*} any
 * @param {*} encodeKeys
 * @param {*} encodeValues
 * @return {*}
 */
export const objectToUrlParams = (obj: Record<string, any>, encodeKeys = true, encodeValues = true): string => {
  try {
    const searchParams = new URLSearchParams();

    for (const key in obj) {
      if (obj.hasOwnProperty.call(obj, key)) {
        const value = obj[key];
        const encodedKey = encodeKeys ? encodeURIComponent(key) : key;
        const encodedValue = encodeValues ? encodeURIComponent(value) : value;
        searchParams.append(encodedKey, encodedValue);
      }
    }
    return searchParams.toString();
  } catch (error) {
    console.error('objectToUrlParams error:', error);
    return '';
  }
};

// 数字千分位格式化
export const formattedNumber = (num: number) => new Intl.NumberFormat(
  'en-US', { maximumFractionDigits: 2 }
).format(num);

/**
 * @description: 获取字符宽度
 * @param {string} text
 * @param {*} options
 */
export const getActualWidthOfChars = (text: string, options = {} as TypeObject) => {
  const { size = 12 } = options;
  const canvas = document.createElement('canvas');
  const ctx: any = canvas.getContext('2d');
  ctx.font = `${size}px`;
  const metrics = ctx.measureText(text);
  return Math.abs(metrics.actualBoundingBoxLeft) + Math.abs(metrics.actualBoundingBoxRight);
};

/**
 * @description: 复制文本到剪贴板
 * @param {string} str
 */
export const copyStr = (str: string) => {
  const input = document.createElement('textarea');
  input.value = str;
  document.body.appendChild(input);
  input.select();
  document.execCommand('Copy');
  document.body.removeChild(input);
};

/**
 * @description: 根据指定字段对元素为对象的数组去重
 * @param {*} arr 要去重的数组
 * @param {*} key 指定的key值
 * @return {*}
 */
export const deduplicationByKey = (arr: TypeObject[], key: string) => {
  const hash: TypeObject = {};
  return arr.reduce((newArr, next) => {
    if (!hash[next[key]]) {
      hash[next[key]] = true;
      newArr.push(next);
    }
    return newArr;
  }, []);
};
